﻿#include "Traingle.h"
#include "Line.h"
#include "../Light/LightMgr.h"
#include <iostream>

int findMid(int a0,int a1,int a2){
	if (a0 > a1)
	{
		if (a1 > a2)
		{
			return 1;
		}else{
			if (a0 > a2)
			{
				return 2;
			}else{
				return 0;
			}
		}
	}else{//a1 > a0
		if (a0 > a2)
		{
			return 0;
		}else{
			if (a1 > a2)
			{
				return 2;
			}else{
				return 1;
			}
		}
	}
}

Traingle::Traingle(Point& p1,Point& p2,Point& p3)
{
	p[0] = p1;
	p[1] = p2;
	p[2] = p3;
	texture = NULL;
}


Traingle::~Traingle(void)
{
}
int getIntersectXOn(float lineK,Point& oneEnd,int y){
	int midX = int(float(y - oneEnd.y) * lineK + oneEnd.x);
	return midX;
}

float linearFloat(int p1,int p2,int theP,float q1,float q2){
	int dp = p2 - p1;
	float dq = q2 - q1;
	if (dp == 0)
	{
		return (q1 + q2) / 2;
	}else{
		return dq * (theP - p1)/ (float)dp + q1;
	}
}
Vec3 linearVec3_Right(Point& p1,Point& p2,Point& theP,Vec3& q1,Vec3& q2){
	float lp = (p1 - p2).length();
	float ltheP = (p1 - theP).length();
	float rp = ltheP / lp;
	float x = q1.x + (q2.x - q1.x) * rp;
	float y = q1.y + (q2.y - q1.y) * rp;
	return Vec3(x,y);
}

Vec3 linearVec3_Wrong(Point& p1,Point& p2,Point& theP,Vec3& q1,Vec3& q2){
	//Below is Wrong!!
	//线性映射是标量的比例关系，而不是分量的比例关系！！！！
	float x = linearFloat(p1.x,p2.x,theP.x,q1.x,q2.x);
	float y = linearFloat(p1.y,p2.y,theP.y,q1.y,q2.y);
	return Vec3(x,y);
}
Vec3 linearVec3(Point& p1,Point& p2,Point& theP,Vec3& q1,Vec3& q2){
	return linearVec3_Right(p1,p2,theP,q1,q2);
	//return linearVec3_Wrong(p1,p2,theP,q1,q2);
}
//for light[tar.x tar.y is known,calc tar.z]
void calcZ(Point& p1,Point& p2,Vec3& tar){
	//if 3d-line is defined,either x or y or z is known,Point is determinated
	if (p1.x - p2.x != 0)
	{
		tar.z = float(tar.x - p1.x)*(p2.z - p1.z)/(p2.x - p1.x) + p1.z ;
	}else if (p1.y - p2.y != 0)
	{
		tar.z = float(tar.y - p1.y)*(p2.z - p1.z)/(p2.y - p1.y) + p1.z ;
	}
}
//pt为平顶三角形的底边对面的顶点,k1 k2为改顶点左右两边的边斜率
void Traingle::horizonMapping( Point& pt,float k1,float k2,int endY,IPixelDrawer* dr,Vec3& uv,Vec3& uv1,Vec3& uv2,Point& pt1,Point& pt2)
{
	int beginY = pt.y;
	int stepY = endY > beginY ? 1 : -1;
	if(endY == beginY)stepY = 0;
	endY = endY + stepY;//黑线来源于四舍的误差,所以endY多一像素(多画一行)弥补
	int beginX,endX;
	do{
		float fbx = getIntersectXOn(k1,pt,beginY);
		float fex = getIntersectXOn(k2,pt,beginY);
		beginX = fbx;
		endX = fex;
		//由于点是离散的！会出现当k1(或k2)很小的时候，移动一像素，交点会在线段外面（可能很大）处，造成出现莫名其妙多出来的图形外面的横条，而且会造成大量计算（卡住）
		//矫正（别用交点在两点之间作为矫正依据，因为存在float计算误差，分情况讨论）
		if (pt1.x > pt.x)
		{
			if (beginX > pt1.x)
			{
				beginX = pt1.x;
			}
			if (beginX < pt.x)
			{
				beginX = pt.x;
			}
		}else{
			if (beginX < pt1.x)
			{
				beginX = pt1.x;
			}
			if (beginX > pt.x)
			{
				beginX = pt.x;
			}
		}
		if (pt2.x > pt.x)
		{
			if (endX > pt2.x)
			{
				endX = pt2.x;
			}
			if (endX < pt.x)
			{
				endX = pt.x;
			}
		}else{
			if (endX < pt2.x)
			{
				endX = pt2.x;
			}
			if (endX > pt.x)
			{
				endX = pt.x;
			}
		}
		Vec3 beginuv = linearVec3(pt,pt1,Point(beginX,beginY),uv,uv1);
		Vec3 enduv = linearVec3(pt,pt2,Point(endX,beginY),uv,uv2);
		//printf("%f,%f-%f,%f | %d-%d,%d,%d\n",beginuv.x,beginuv.y,enduv.x,enduv.y,beginX,beginY,endX,endY);
		int dx = endX - beginX;
		dx = dx > 0 ? dx: -dx;//取dx的绝对值
		Vec3 stepuv;
		int stepX = endX > beginX ? 1 : -1;
		if (dx != 0)
		{
			stepuv.x = (enduv.x - beginuv.x) / dx;
			stepuv.y = (enduv.y - beginuv.y) / dx;//因为是[x的绝对值]递增，所以v也是和x呈线性关系 
		}
		if(endX == beginX)stepX = 0;
		endX = endX + stepX;
		//<1><light>two ends
		Vec3 end1(beginX,beginY,0),end2(endX,beginY,0);
		do 
		{
			if (texture)
			{
				COLOR color = texture->getPixel(beginuv);
				//<2><light>calc Z of end1,end2
				calcZ(pt,pt1,end1);
				calcZ(pt,pt2,end2);
				Vec3 cur(beginX,beginY);
				float lEnds = end1.x - end2.x;//(end1 - end2).length();
				if (lEnds == 0)
				{
					cur.z = 0;
				}else{
					float r1 = (end1.x - beginX)/lEnds;  
					cur.z = end1.z + (end2.z - end1.z) * r1;
				}
				//Z Buffer
				if (dr->testZBuffer(beginX,beginY,cur.z))
				{
					//<3><light>calc light
					LightMgr::getInstance()->getColor(Vec3(beginX,beginY),_normal,color);
					dr->drawPixel(beginX,beginY,color);
				}
				beginuv += stepuv; 
			}else{
				dr->drawPixel(beginX,beginY,RGBA(0,0,255,255));
			}
			beginX += stepX;
		} while (beginX != endX);
		beginY += stepY;
	}while (beginY != endY);
}

bool Traingle::isBackFace(IPixelDrawer* dr){
	switch (dr->getCullFrontFace())
	{
	case IPixelDrawer::CullNone:
		return false;
		break;
	case IPixelDrawer::CCW:
		return _normal.z < 0;
		break;
	case IPixelDrawer::CW:
		return _normal.z < 0;
		break;
	default:
		return false;
		break;
	}
}
void Traingle::draw( IPixelDrawer* dr )
{
	if (texture)
	{
		if (isBackFace(dr))
		{
			//printf("\tFaceBack\n");
			return;
		}
		//printf("\tFaceFront\n");
		int mid = findMid(p[0].y,p[1].y,p[2].y);
		Point *ptr1 = NULL,*ptr2 = NULL;
		Vec3 *uvptr1 = NULL,*uvptr2 = NULL;
		for (int i = 0; i < 3; i++)
		{
			if (i != mid)
			{
				if (ptr1)
				{
					ptr2 = &p[i];
					uvptr2 = &uv[i];
				}else{
					ptr1 = &p[i];
					uvptr1 = &uv[i];
				}
			}
		}
		Point p1 = *ptr1, p2 = *ptr2, pm = p[mid];
		Vec3 uv1 = *uvptr1, uv2 = *uvptr2, uvm = uv[mid]; 
		if(abs(pm.y - p1.y) < FLT_EPSILON ){
			if (abs(pm.y - p2.y) > FLT_EPSILON)
			{
				float k2 = float(p2.x - pm.x) / (p2.y - pm.y);
				float km = float(p2.x - p1.x) / (p2.y - p1.y);
				horizonMapping(p2,km,k2,pm.y,dr,uv2,uv1,uvm,p1,pm);
			}
		}else if(abs(pm.y - p2.y) < FLT_EPSILON ){
			if(abs(pm.y - p1.y) > FLT_EPSILON ){
				float k1 = float(p1.x - pm.x) / (p1.y - pm.y);
				float km = float(p2.x - p1.x) / (p2.y - p1.y);
				horizonMapping(p1,km,k1,pm.y,dr,uv1,uv2,uvm,p2,pm);
			}
		}else{
			float k1 = float(p1.x - pm.x) / (p1.y - pm.y);
			float k2 = float(p2.x - pm.x) / (p2.y - pm.y);
			float km = float(p2.x - p1.x) / (p2.y - p1.y);
			horizonMapping(p1,km,k1,pm.y,dr,uv1,uv2,uvm,p2,pm);
			horizonMapping(p2,km,k2,pm.y,dr,uv2,uv1,uvm,p1,pm);
		}
	}
	if (!texture || _bShowBorder)
	{
		Line l1(p[0],p[1]);	
		Line l2(p[1],p[2]);	
		Line l3(p[2],p[0]);	
		l1.draw(dr);
		l2.draw(dr);
		l3.draw(dr);
	}
}

void Traingle::mulMat( Mat4& mat )
{
	p[0].mulMat(mat);
	p[1].mulMat(mat);
	p[2].mulMat(mat);
	calcNormal();
}

void Traingle::setTexture( Texture* texture,Vec3& uv0,Vec3& uv1,Vec3& uv2 )
{
	this->texture = texture; 
	this->uv[0] = uv0;
	this->uv[1] = uv1;
	this->uv[2] = uv2;
}

void Traingle::calcNormal()
{
	Vec3 l1 = (p[0] - p[1]).toVec3();
	Vec3 l2 = (p[1] - p[2]).toVec3();
	_normal = l1.cross(l2);
	_normal /= _normal.length();
}




